package org.example.transaction;

import org.example.MainChain;
import org.example.utils.StringUtils;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;

public class Transaction {

    public String transactionId;

    public PublicKey sender;

    public PublicKey recipient;

    public float value;

    public byte[] signature;

    public ArrayList<TransactionInput> inputs = new ArrayList<TransactionInput>();

    public ArrayList<TransactionOutput> outputs = new ArrayList<TransactionOutput>();

    private static int sequence = 0;

    public Transaction(PublicKey from, PublicKey to, float value, ArrayList<TransactionInput> inputs) {
        this.sender = from;
        this.recipient = to;
        this.value = value;
        this.inputs = inputs;
    }

    // This Calculates the transaction hash (which will be used as its Id)
    private String calulateHash() {
        sequence++; //increase the sequence to avoid 2 identical transactions having the same hash
        return StringUtils.applySha256(
                StringUtils.getStringFromKey(sender) +
                        StringUtils.getStringFromKey(recipient) +
                        Float.toString(value) + sequence
        );
    }

    //Signs all the data we dont wish to be tampered with.
    public void generateSignature(PrivateKey privateKey) {
        String data = StringUtils.getStringFromKey(sender) + StringUtils.getStringFromKey(recipient) + Float.toString(value)	;
        this.signature = StringUtils.applyECDSASig(privateKey,data);
    }
    //Verifies the data we signed hasnt been tampered with
    public boolean verifiySignature() {
        String data = StringUtils.getStringFromKey(sender) + StringUtils.getStringFromKey(recipient) + Float.toString(value)	;
        return StringUtils.verifyECDSASig(sender, data, signature);
    }

    public float getInputValue() {
        float total = 0;
        for (TransactionInput input : inputs) {
            if(input.UTXO == null) continue;
            total += input.UTXO.value;
        }
        return total;
    }

    public float getOutputValue() {
        float total = 0;
        for (TransactionOutput output : outputs) {
            total += output.value;
        }
        return total;
    }

    public boolean processTransaction() {
        if(verifiySignature() == false) {
            System.out.println("Transaction signature verification failed");
            return false;
        }

        for(TransactionInput input : inputs) {
            input.UTXO = MainChain.UTXOs.get(input.transactionOutputId);
        }

        if(getInputValue() < MainChain.minimumTransaction) {
            System.out.println("Transaction input value less than minimum transaction");
            return false;
        }

        float leftOver = getInputValue() - value;
        this.transactionId = calulateHash();
        outputs.add(new TransactionOutput(recipient, value, transactionId));
        outputs.add(new TransactionOutput(sender, leftOver, transactionId));

        for(TransactionOutput output : outputs) {
            MainChain.UTXOs.put(output.id, output);
        }

        for (TransactionInput input : inputs) {
            if(input.UTXO == null) continue;
            MainChain.UTXOs.remove(input.UTXO.id);
        }

        return true;

    }
}
